﻿#region 命名空间

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using Sharp.DataTransferObject;
using Sharp.Domain.PlatformBoundedContext.MessageModule.Aggregates;
using Sharp.Domain.PlatformBoundedContext.MessageModule.Services;
using Sharp.Domain.PlatformBoundedContext.PermissionModule.Aggregates;
using Sharp.Domain.PlatformBoundedContext.UserModule.Aggregates;
using Sharp.Domain.Repositories;
using Sharp.Infrastructure;
using Sharp.Infrastructure.Crosscutting;
using Sharp.Infrastructure.Search;
using Sharp.Events;
using Sharp.Domain.PlatformBoundedContext.UserModule.Events;
using System.Threading.Tasks;
using Sharp.Domain.PlatformBoundedContext.PermissionModule.Events;
using Sharp.Domain.PlatformBoundedContext.PersonModule.Aggregates;
using Sharp.Infrastructure.IoC;
using Sharp.ServiceContracts;
using Sharp.Domain.PlatformBoundedContext.StoragerModule.Events;
using Sharp.Domain.PlatformBoundedContext.StoragerModule.Files;

#endregion

namespace Sharp.Application.Platform
{
    /// <inheritdoc />
    /// <summary>
    ///     用户服务
    /// </summary>
    public partial class UserServiceImpl : ApplicationService, Sharp.ServiceContracts.IUserService
    {
        #region 私有字段

        private readonly Sharp.Domain.PlatformBoundedContext.UserModule.Services.IUserService _domainService;
        private readonly IMessageService _messageServices;

        private readonly IPermissionRepository _permissionRepository;
        private readonly IRoleRepository _roleRepository;
        private readonly IUserRepository _userRepository;
        private readonly IUserLogRepository _userLogRepository;
        private readonly IMessageRepository _messageRepository;
        private readonly INoticeRepository _noticeRepository;
        private readonly IResetPasswordRepository _resetPasswordRepository;
        #endregion

        #region 构造函数

        /// <summary>
        ///     初始化一个新的<c>UserServiceImpl</c>实例。
        /// </summary>
        /// <param name="context"></param>
        /// <param name="userRepository"></param>
        /// <param name="roleRepository"></param>
        /// <param name="permissionRepository"></param>
        /// <param name="userLogRepository"></param>
        /// <param name="messageRepository"></param>
        /// <param name="noticeRepository"></param>
        /// <param name="resetPasswordRepository"></param>
        /// <param name="domainService"></param>
        /// <param name="messageServices"></param>
        public UserServiceImpl(IRepositoryContext context,
            IUserRepository userRepository,
            IRoleRepository roleRepository,
            IPermissionRepository permissionRepository,
            IUserLogRepository userLogRepository,
            IMessageRepository messageRepository,
            INoticeRepository noticeRepository,
            IResetPasswordRepository resetPasswordRepository,

            Sharp.Domain.PlatformBoundedContext.UserModule.Services.IUserService domainService,
            IMessageService messageServices)
            : base(context)
        {
            _userRepository = userRepository;
            _roleRepository = roleRepository;
            _permissionRepository = permissionRepository;
            _userLogRepository = userLogRepository;

            _messageRepository = messageRepository;
            _noticeRepository = noticeRepository;
            _resetPasswordRepository = resetPasswordRepository;

            _domainService = domainService;
            _messageServices = messageServices;

        }

        #endregion

        #region 保存用户

        /// <inheritdoc />
        /// <summary>
        /// 新增用户
        /// </summary>
        /// <param name="userdto">包含了客户信息的数据传输对象。</param>
        [Logging("保存用户，{@p0.UserName}")]
        public Result SaveUser(UserDTO userdto)
        {
            if (userdto == null)
                throw new ArgumentNullException(nameof(userdto));

            var res = new Result();


            if (userdto.Id.IsEmpty())
            {
                if (userdto.Password.IsNullOrEmpty())
                {
                    res.Err = "密码不能为空。";
                    return res;
                }
                var user = userdto.To<User>();

                EventBus.Instance.Publish(new StorageChangedEventData()
                {
                    EventSource = user,
                    File = userdto.Face,
                    HandlerData = (id) =>
                    {
                        user.Face = id;
                    }
                });

                user.Password = userdto.Password.Sha1();

                if (user.Profile == null)
                {
                    user.Profile = new UserProfile();
                }
                user.Profile.RegisteredTime = DateTime.Now;
                if (user.Person == null)
                {
                    user.Person = new Employee()
                    {
                        EntityState = Domain.Model.EntityStateOperate.IsDraft
                    };
                }
                user.Profile.LastPasswordChangedDate = null;
                if (userdto.RoleIds != null)
                {
                    user.Roles = _roleRepository.GetList(x => userdto.RoleIds.Contains(x.Id));
                }
                _userRepository.Add(user);

                userdto.Id = user.Id;
            }
            else
            {
                var info = _userRepository.Get(userdto.Id, x => x.Roles, x => x.Profile, x => x.Person);
                if (info.Profile == null)
                {
                    info.Profile = new UserProfile();
                }
                if (info.Person == null)
                {
                    info.Person = new Employee()
                    {
                        EntityState = Domain.Model.EntityStateOperate.IsDraft
                    };
                }

                info.Profile.Comment = userdto.Profile.Comment;
                info.Profile.IsApproved = userdto.Profile.IsApproved;
                info.Profile.IsLockedOut = userdto.Profile.IsLockedOut;
                info.Profile.QQ = userdto.Profile.QQ;
                info.Profile.Wechart = userdto.Profile.Wechart;
                info.Profile.PasswordQuestion = userdto.Profile.PasswordQuestion;

                info.Email = userdto.Email;
                if (!userdto.Password.IsNullOrEmpty())
                {
                    info.Password = userdto.Password.Sha1();
                }


                info.Roles.Clear();

                if (userdto.RoleIds != null)
                    info.Roles = _roleRepository.GetList(x => userdto.RoleIds.Contains(x.Id));


                EventBus.Instance.Publish(new StorageChangedEventData()
                {
                    EventSource = info,
                    File = userdto.Face,
                    HandlerData = (id) =>
                    {
                        info.Face = id;
                    }
                });

                _userRepository.AutoUpdateFields(info).Update(info);
            }

            Context.Commit();


            EventBus.Instance.Publish(new PermissionChangedEventData()
            {
                UserId = userdto.Id
            });

            return res;
        }

        #endregion

        #region 校验指定的客户用户名与密码是否一致。
        /// <summary>
        ///     校验指定的客户用户名与密码是否一致。
        /// </summary>
        /// <param name="userName">客户用户名。</param>
        /// <param name="password">客户密码。</param>
        /// <returns>如果校验成功，则返回true，否则返回false。</returns>
        public bool ValidateUser(string userName, string password)
        {
            if (string.IsNullOrEmpty(userName))
                throw new ArgumentNullException(nameof(userName));
            if (string.IsNullOrEmpty(password))
                throw new ArgumentNullException(nameof(password));

            return _userRepository.CheckPassword(userName, password.Sha1());
        }
        #endregion

        /// <inheritdoc />
        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="userDTO"></param>
        /// <returns></returns>
        //[Logging("用户，{@p0.UserName} 登录。")]
        public Result<UserDTO> TryLogin(UserDTO userDTO)
        {
            User user = userDTO.To<User>();

            var res = _domainService.TryLogin(user);


            var output = new Result<UserDTO>();
            output.Err = res.Err;
            output.Data = res.Data.To<UserDTO>();

            return output;
        }

        /// <inheritdoc />
        /// <summary>
        /// 禁用用户
        /// </summary>
        /// <param name="user">需要禁用的用户。</param>
        [Logging("禁用用户：{@p0.UserName}")]
        public bool DisableUser(UserDTO user)
        {
            if (user == null)
                throw new ArgumentNullException(nameof(user));
            User userinfo = null;
            if (user.Id != 0)
                userinfo = _userRepository.Get(user.Id, x => x.Profile);
            else if (!string.IsNullOrEmpty(user.UserName))
                userinfo = _userRepository.GetUserByName(user.UserName);
            else if (!string.IsNullOrEmpty(user.Email))
                userinfo = _userRepository.GetUserByEmail(user.Email);
            else
                throw new ArgumentNullException(nameof(user), "未赋值");

            if (userinfo.Profile == null)
            {
                userinfo.Profile = new UserProfile();
            }
            userinfo.Profile.IsLockedOut = true;

            _userRepository.Update(userinfo);
            Context.Commit();

            return true;
        }

        /// <inheritdoc />
        /// <summary>
        ///     启用用户。
        /// </summary>
        /// <param name="userdto">需要启用的用户。</param>
        /// <returns>如果成功，则返回true，否则返回false。</returns>
        [Logging("启用用户：{@p0.UserName}")]
        public bool EnableUser(UserDTO userdto)
        {
            if (userdto == null)
                throw new ArgumentNullException(nameof(userdto));
            User user = null;
            if (userdto.Id != 0)
            {
                user = _userRepository.Get(userdto.Id, x => x.Profile);
            }
            else if (!string.IsNullOrEmpty(userdto.UserName))
            {
                user = _userRepository.GetUserByName(userdto.UserName);
            }
            else if (!string.IsNullOrEmpty(userdto.Email))
            {
                user = _userRepository.GetUserByEmail(userdto.Email);
            }
            else
            {
                throw new ArgumentNullException(nameof(userdto), "未赋值");
            }
            if (user.Profile == null)
            {
                user.Profile = new UserProfile();
            }
            user.Profile.IsLockedOut = false;
            _userRepository.Update(user);
            Context.Commit();

            return true;
        }


        /// <inheritdoc />
        /// <summary>
        ///     删除用户。
        /// </summary>
        /// <param name="userids">需要删除的用户的Id。</param>
        public Result DeleteUsers(IList<int> userids)
        {
            var res = new Result();
            if (userids == null)
            {
                res.Err = "参数不可为空。";
                return res;
            }

            _userRepository.Remove(userids);

            Context.Commit();

            return res;
        }

        /// <inheritdoc />
        /// <summary>
        ///     根据用户的全局唯一标识获取用户信息。
        /// </summary>
        /// <param name="id">用户的全局唯一标识。</param>
        /// <returns>包含了用户信息的数据传输对象。</returns>
        public Result<UserDTO> GetUserByKey(int id)
        {
            var user = _userRepository.Get(id, x => x.Roles, x => x.Profile);
            var employeeImpl = ServiceLocator.Instance.GetService<IEnterpriseService>();
            user.Face = SharpStorager.Context.Get(user.Face);
            var res = new Result<UserDTO>(user.To<UserDTO>())
            {
                Data =
                {
                    Person = employeeImpl.GetEmployeeInfo(id)
                }
            };
            return res;
        }

        /// <inheritdoc />
        /// <summary>
        ///     根据用户的电子邮件地址获取用户信息。
        /// </summary>
        /// <param name="email">用户的电子邮件地址。</param>
        /// <returns>包含了用户信息的数据传输对象。</returns>
        public Result<UserDTO> GetUserByEmail(string email)
        {
            if (string.IsNullOrEmpty(email))
                throw new ArgumentException("email");
            var user = _userRepository.GetUserByEmail(email);

            var res = new Result<UserDTO>(user.To<UserDTO>());
            return res;
        }

        /// <inheritdoc />
        /// <summary>
        ///     根据用户的用户名获取用户信息。
        /// </summary>
        /// <param name="userName">用户的用户名。</param>
        /// <returns>包含了用户信息的数据传输对象。</returns>
        public Result<UserDTO> GetUserByName(string userName)
        {
            if (string.IsNullOrEmpty(userName))
                throw new ArgumentException("userName");
            var user = _userRepository.GetUserByName(userName);

            var res = new Result<UserDTO>(user.To<UserDTO>());
            return res;
        }

        /// <inheritdoc />
        /// <summary>
        /// 关联用户
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="roleIds"></param>
        /// <returns></returns>
        public Result RelationUser(int userId, int[] roleIds)
        {
            if (userId.IsEmpty())
            {
                return new Result() { Err = "用户Id参数不能为空。" };
            }
            var res = _domainService.RelationUser(userId, roleIds);

            Context.Commit();
            return res;
        }


        /// <summary>
        /// 关联用户到工作组
        /// </summary>
        /// <param name="groupId"></param>
        /// <param name="userIds"></param>
        /// <returns></returns>
        public Result RelationUserForGroup(int groupId, int[] userIds)
        {
            if (userIds == null)
            {
                return new Result() { Err = "用户Id参数不能为空。" };
            }
            var res = _domainService.RelationUserForGroup(groupId, userIds);

            Context.Commit();
            return res;
        }


        /// <summary>
        ///     获取分页数据
        /// </summary> 
        /// <returns></returns>
        public PagedResult<UserDTO> GetUsersPaged(PagingFeature pf)
        {
            Expression<Func<User, bool>> predicate = x => true;
            //var IS = new[] { 1, 2, 3, 4 };
            //predicate = x => x.Roles.Any(r => IS.Contains(r.Id));

            var paged = _userRepository.Pagging(pf, predicate, x => x.Profile);

            return paged.To<User, UserDTO>();
        }

        ///// <summary>
        ///// 显示是否在工作组中的用户
        ///// </summary>
        ///// <param name="pf"></param>
        ///// <param name="groupId"></param>
        ///// <param name="includeGroup">是否在工作组中</param>
        ///// <returns></returns>
        //public PagedResult<UserDTO> GetUsersPagedForGroup(PagingFeature pf, int groupId, bool includeGroup)
        //{
        //    Expression<Func<User, bool>> predicate = x => true;
        //    if (includeGroup)
        //    {
        //        predicate = predicate.And(x => x.Person.Groups.Any(u => u.Id == groupId));
        //    }
        //    else
        //    {
        //        predicate = predicate.And(x => x.Person.Groups.All(u => u.Id != groupId));
        //    }
        //    var paged = _userRepository.Pagging(pf, predicate, x => x.Person);

        //    return paged.To<User, UserDTO>();
        //}

  



        /// <summary>
        ///     获取用户权限
        /// </summary>
        /// <param name="username"></param>
        /// <returns></returns>
        public IEnumerable<PermissionDTO> GetPermissionByUserName(string username)
        {
            if (username.IsNullOrEmpty())
            {
                throw new NullReferenceException("参数username不能为空。");
            }

            return _domainService.GetPermissionByUserName(username).To<Permission, PermissionDTO>();
        }



        public bool ChangePassword(string username, string oldPassword, string newPassword)
        {
            throw new NotImplementedException();
        }

        public bool ChangePasswordQuestionAndAnswer(string username, string password, string newPasswordQuestion,
            string newPasswordAnswer)
        {
            throw new NotImplementedException();
        }



        public bool UnlockUser(string userName)
        {
            throw new NotImplementedException();
        }


        #region 重置密码
        /// <summary>
        /// 重置密码
        /// </summary>
        /// <param name="userdto">包含了客户信息的数据传输对象。</param>
        /// <returns>结果集</returns>
        public Result ResetPassword(ResetPasswordDTO userdto)
        {
            if (userdto == null)
                throw new ArgumentNullException("userdto");

            var res = new Result();
            var reset = _resetPasswordRepository.Get(x => x.Token == userdto.AntiForgeryToken);
            if (reset == null)
            {
                res.Err = "请重新申请重置密码。";
                return res;
            }

            var persisted = _userRepository.GetUserByEmail(reset.EMail);
            if (persisted != null)
            {
                if (userdto.PassWord.IsNullOrEmpty())
                {
                    var guid = Guid.NewGuid();
                    var newPassword = guid.ToString().Substring(0, 5);
                    persisted.Password = newPassword.Sha1();
                    _userRepository.Update(persisted);

                    res.Message = newPassword;
                }
                else
                {
                    persisted.Password = userdto.PassWord.Sha1();
                    _userRepository.Update(persisted);
                }
            }
            else
            {
                res.Err = "输入邮箱不存在";
                return res;
            }
            Context.Commit();

            return res;
        }
        public Result ResetPassword(string email, string passWord)
        {
            if (email == null)
                throw new ArgumentNullException("Email");

            var res = new Result();
            var persisted = _userRepository.GetUserByEmail(email);
            if (persisted != null)
            {
                if (passWord.IsNullOrEmpty())
                {
                    var guid = Guid.NewGuid();
                    var newPassword = guid.ToString().Substring(0, 5);
                    persisted.Password = newPassword.Sha1();
                    _userRepository.Update(persisted);

                    res.Message = newPassword;
                }
                else
                {
                    persisted.Password = passWord.Sha1();
                    _userRepository.Update(persisted);
                }
            }
            else
            {
                res.Err = "输入邮箱不存在";
                return res;
            }
            Context.Commit();

            return res;
        }
        #endregion

        #region 忘记密码
        /// <summary>
        /// 忘记密码
        /// </summary>
        /// <param name="dto"></param>
        /// <returns></returns>
        public Result ForgotPassword(ForgotPasswordDTO dto)
        {
            var res = new Result();
            var user = _userRepository.GetUserByEmail(dto.Email);
            if (user == null)
            {
                res.Err = "用户不存在。";
                return res;
            }
            else if (user.Profile.IsLockedOut)
            {

                res.Err = "此账号已经锁定。";
                return res;
            }
            var data = new ResetPasswordEventData();
            data.Email = dto.Email;
            data.Body = "<a href='http://" + dto.CallBackUrl + "/ResetPassword/" + dto.Token + "' target=\"_blank\">请尽快点击此链接重置密码！</a>";
            EventBus.Instance.Publish(data);

            res.Message = "邮件已经发出，请稍后查收。";
            return res;

        }

        /// <summary>
        /// 增加失败重试次数
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        public Result<int> IncrementAccessFailedCount(int userId)
        {
            var user = _userRepository.Get(userId, x => x.Profile);
            if (user.Profile != null)
            {
                user.Profile.AccessFailedCount += 1;
                _userRepository.Context.Commit();

                return new Result<int>() { Data = user.Profile.AccessFailedCount };
            }
            return new Result<int>() { Data = 0 };
        }

        public void ResetAccessFailedCount(int userId)
        {
            var user = _userRepository.Get(userId, x => x.Profile);
            if (user.Profile != null)
            {
                user.Profile.AccessFailedCount = 0;
                _userRepository.Context.Commit();
            }
        }

        public void SetLockoutEnabled(int userId, bool enabled)
        {
            var user = _userRepository.Get(userId, x => x.Profile);
            if (user.Profile != null)
            {
                user.Profile.IsLockedOut = enabled;
                _userRepository.Context.Commit();
            }
        }

        public void SetLockoutEndDate(int userId, DateTimeOffset lockoutEnd)
        {
            var user = _userRepository.Get(userId, x => x.Profile);
            if (user.Profile != null)
            {
                user.Profile.LastLockoutDate = lockoutEnd.DateTime;
                _userRepository.Context.Commit();
            }
        }
        #endregion


    }
}